pub mod cmd;
pub mod custom;
pub mod task;

use base::{
    base::{Base, VBase, VMap},
    client, err, err_value,
    file::JsonFile,
    ok, throw_err, ECode, MapSv, RNull, Resp, VecV,
};
use bytes::Bytes;
use cmd::plc::Plc;
use moka::future::Cache;
use serde_json::{json, Value};
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;
use tracing::{instrument, warn};
use warp::{
    reject::Rejection,
    reply::{Json, WithStatus},
};

pub use cmd::cache::CfgCache;
pub use custom::do_custom;
pub use task::{do_task, is_active};

pub type Client = Arc<client::Client>;
pub type MSS = HashMap<String, String>;
pub type MSI = HashMap<String, i64>;
pub type LockFile = RwLock<bool>;
pub type SvCache = Cache<String, Value>;

pub const ST_WAIT: i64 = 0;
pub const ST_ACTIVE: i64 = 1;
pub const ST_DONE: i64 = 2;
pub const ST_DOING: i64 = 3;

#[instrument(skip_all, name = "get")]
pub async fn do_get(
    model: String,
    cmd: String,
    cache: SvCache,
) -> Result<WithStatus<Json>, Rejection> {
    let msg = format!("get: {:?}/{:?}", model, cmd);

    match cmd::do_get(&model, &cmd, cache).await {
        Ok(v) => Ok(base::ret_ok(&v)),
        Err(e) => {
            warn!("error: {:?}, {:?}", e, msg);
            Ok(base::ret_err(&e, None))
        }
    }
}

#[instrument(skip_all, name = "s")]
pub async fn do_cmd(
    data: Bytes,
    model: String,
    cmd: String,
    lock: &LockFile,
    cache: SvCache,
    real: SvCache,
    client: &Client,
) -> Result<WithStatus<Json>, Rejection> {
    let val = match base::do_byte(data) {
        Ok(v) => v,
        Err(e) => {
            warn!("{:?}", e);
            return Ok(base::ret_err(&e, None));
        }
    };

    let msg = format!("{:?}/{:?}, {:?}", model, cmd, val);

    match cmd::doit(val, &model, &cmd, lock, cache, real, client).await {
        Ok(v) => Ok(base::ret_ok(&v)),
        Err(e) => {
            warn!("error: {:?}, {:?}", e, msg);
            Ok(base::ret_err(&e, None))
        }
    }
}

pub fn get_dir() -> String {
    if cfg!(windows) {
        "./config".to_string()
    } else {
        "/files/config".to_string()
    }
}

pub fn get_file(file: &str) -> String {
    let dir = get_dir();
    format!("{dir}/{file}.json")
}

pub fn init_cfg() {
    let dir = get_dir();

    let plc = format!("{dir}/plc");
    let it = format!("{dir}/it");
    let work = format!("{dir}/work");

    let _ = JsonFile::make_dir(&plc);
    let _ = JsonFile::make_dir(&it);
    let _ = JsonFile::make_dir(&work);
}

pub fn mod_dir(md: &str, f: &str) -> String {
    let d = get_dir();
    format!("{d}/{md}/{f}")
}

pub fn plc_dir(f: &str) -> String {
    mod_dir("plc", f)
}

pub fn it_dir(f: &str) -> String {
    mod_dir("it", f)
}

pub fn work_dir(f: &str) -> String {
    mod_dir("work", f)
}
